<script lang="ts">
	import { onMount, tick, untrack } from 'svelte';
	import { debounce } from 'lodash-es';
	import { type Placement } from './types';
	import { cubicOut } from 'svelte/easing'; // 导入缓动函数

	interface Props {
		popoverRef?: HTMLElement;
		placement?: Placement;
		translateX?: number;
		translateY?: number;
		triggerRect: {
			top: number;
			left: number;
			bottom: number;
			right: number;
			width: number;
			height: number;
		};
		offset?: number;
		contentClass?: string;
		transitionDuration?: number;
		style?: string;
		repositionFlag?: number;
		children?: any; // Svelte 5 slot
	}

	let {
		popoverRef = $bindable(),
		placement = 'auto',
		translateX = 0,
		translateY = 0,
		triggerRect,
		offset = 2,
		contentClass = '',
		transitionDuration = 150,
		style = '',
		repositionFlag = 0,
		children
	}: Props = $props();

	// 状态管理
	let position = $state({ top: 0, left: 0 });
	let calculatedPlacement = $state<Placement>('bottom');
	const SCALE_FACTOR = transitionDuration === 0 ? 1 : 0.8;

	/**
	 * 计算弹出框的位置和方向 - 暴露给外部调用的方法
	 */
	export const recalculatePosition = async () => {
		await calculatePosition();
	};

	/**
	 * 计算弹出框的位置和方向
	 */
	const calculatePosition = async () => {
		if (!popoverRef) return;

		// 等待下一帧确保尺寸已更新
		await tick();

		// 获取触发元素和视口的位置信息
		const popoverRect = popoverRef.getBoundingClientRect();
		// 使用缩放前的真实尺寸进行计算
		const pWidth = popoverRect.width / SCALE_FACTOR;
		const pHeight = popoverRect.height / SCALE_FACTOR;
		const viewport = {
			width: window.innerWidth,
			height: window.innerHeight
		};

		let finalPlacement = placement;
		if (placement === 'auto') {
			const spaceBottom = viewport.height - triggerRect.bottom;
			const spaceTop = triggerRect.top;

			// 优先下，其次上
			if (spaceBottom >= pHeight + offset) {
				finalPlacement = 'bottom';
			} else if (spaceTop >= pHeight + offset) {
				finalPlacement = 'top';
			} else {
				finalPlacement = 'bottom';
			}
		}
		calculatedPlacement = finalPlacement;

		// 🔥 2. 核心位置计算逻辑
		let top = 0;
		let left = 0;
		// 解析主方向和对齐方式 (例如 'bottom-start' -> base='bottom', align='start')
		const [base, align] = finalPlacement.split('-') as [string, string?];
		// 根据方向计算位置
		// A. 计算主轴位置 (top/left)
		switch (base) {
			case 'top':
				top = triggerRect.top - pHeight - offset;
				break;
			case 'bottom':
				top = triggerRect.bottom + offset;
				break;
			case 'left':
				left = triggerRect.left - pWidth - offset;
				break;
			case 'right':
				left = triggerRect.right + offset;
				break;
		}
		// B. 计算交叉轴对齐 (align)
		if (base === 'top' || base === 'bottom') {
			// 水平对齐
			if (align === 'start') {
				// 左对齐: Popover左边 = Trigger左边
				left = triggerRect.left;
			} else if (align === 'end') {
				// 右对齐: Popover右边 = Trigger右边 -> Popover左边 = Trigger右边 - Popover宽
				left = triggerRect.right - pWidth;
			} else {
				// 居中 (默认)
				left = triggerRect.left + (triggerRect.width - pWidth) / 2;
			}
		} else if (base === 'left' || base === 'right') {
			// 垂直对齐
			if (align === 'start') {
				// 顶对齐
				top = triggerRect.top;
			} else if (align === 'end') {
				// 底对齐
				top = triggerRect.bottom - pHeight;
			} else {
				// 居中
				top = triggerRect.top + (triggerRect.height - pHeight) / 2;
			}
		}

		position = { top, left };
	};

	/**
	 * 处理窗口大小变化
	 */
	const handleResize = debounce(() => {
		calculatePosition();
	}, 200);

	/**
	 * 处理动画
	 */

	/**
	 * 自定义组合过渡：同时实现缩放和淡入淡出
	 * @param node 元素节点
	 * @param params 过渡参数（时长、缓动函数等）
	 */
	const combinedTransition = (
		node: HTMLElement,
		params: {
			duration: number;
			easing: (t: number) => number;
			startScale: number; // 入场起始缩放比例
		}
	) => {
		const { duration, easing, startScale } = params;

		return {
			duration,
			easing,
			// 生成CSS动画：同时控制opacity和transform（缩放）
			css: (t: number) => {
				// t的范围是0~1：0表示开始状态，1表示结束状态
				const opacity = t; // 淡入：从0到1；淡出：从1到0（自动反向）
				const scale = startScale + (1 - startScale) * t; // 缩放：从startScale到1

				return `
					opacity: ${opacity};
					transform: scale(${scale});
				`;
			}
		};
	};

	onMount(() => {
		calculatePosition();
		// document.addEventListener('click', handleClickOutside);
		window.addEventListener('resize', handleResize);
		// window.addEventListener('scroll', handleScroll);

		// 组件卸载时清理事件监听
		return () => {
			// document.removeEventListener('click', handleClickOutside);
			window.removeEventListener('resize', handleResize);
			// window.removeEventListener('scroll', handleScroll);
		};
	});

	// 监听 repositionFlag 变化，重新计算位置
	$effect(() => {
		repositionFlag; // 依赖收集
		untrack(() => calculatePosition());
	});
</script>

<div
	bind:this={popoverRef}
	class={`popover ${contentClass}`}
	style={`
		top: ${position.top + (translateY || 0)}px;
		left: ${position.left + (translateX || 0)}px;
		${style}
		`}
	role="tooltip"
	in:combinedTransition={{
		duration: transitionDuration,
		easing: cubicOut,
		startScale: SCALE_FACTOR
	}}
>
	{@render children?.()}
</div>

<style scoped lang="scss">
	.popover {
		min-width: 200px;
		min-height: 100px;
		position: absolute;
		z-index: 1000;
		pointer-events: auto; /* 允许接收事件 */
		border-radius: var(--border-radius);
		background-color: var(--color-page);
		box-shadow: 3px 3px 8px 0px var(--color-shadow);
		box-sizing: border-box;
		word-wrap: break-word;
		// border: var(--border);

		/* 关键：文字渲染优化 */
		transform-style: preserve-3d; /* 保持3D空间渲染一致性 */
		backface-visibility: hidden; /* 隐藏背面，减少渲染计算 */
		will-change: transform, opacity; /* 告诉浏览器提前准备动画渲染 */
		-webkit-font-smoothing: subpixel-antialiased; /* 优化字体抗锯齿 */
	}
</style>
